/*
 * Copyright 2008 Zoundry LLC, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 */
package org.zoundry.gaedt.services.applications.starters;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IPath;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.python.pydev.debug.core.Constants;
import org.zoundry.gaedt.core.models.application.ZGaeApplicationModel;
import org.zoundry.gaedt.core.util.ZGaeCloser;
import org.zoundry.gaedt.core.util.ZGaeFileUtil;
import org.zoundry.gaedt.sdk.IZGaeSDKConstants;
import org.zoundry.gaedt.sdk.ZGaeSDK;
import org.zoundry.gaedt.services.ZGaeServicesActivator;
import org.zoundry.gaedt.services.applications.ZGaePythonApplication;
import org.zoundry.gaedt.userinput.ZGaeUserInputService;


/**
 * Utils for dealing with pydev launches.
 */
public final class ZGaePydevLaunchUtil
{
    /**
     * Private util class c'tor.
     */
    private ZGaePydevLaunchUtil()
    {
    }

    /**
     * Grabs the launch manager from the DebugPlugin.
     *
     * @return  The default launch manager.
     */
    public static ILaunchManager getLaunchManager()
    {
        return DebugPlugin.getDefault().getLaunchManager();
    }

    /**
     * Returns the launch configuration type associated with the BUnit Launch
     * Configuration.
     */
    public static ILaunchConfigurationType getLaunchConfigType()
    {
        return getLaunchManager().getLaunchConfigurationType(Constants.ID_PYTHON_REGULAR_LAUNCH_CONFIGURATION_TYPE);
    }

    /**
     * Create a launch configuration for the given python Google App Engine
     * application instance.
     *
     * @param aApplication
     * @param aSaveConfig
     */
    public static ILaunchConfiguration createLaunchConfiguration(ZGaePythonApplication aApplication,
            boolean aSaveConfig) throws Exception
    {
        ILaunchConfigurationType configType = getLaunchConfigType();
        ILaunchConfiguration config = null;

        // Create a new launch configuration working copy for this type
        String launchName = "__gae__" + aApplication.getModel().getName();
        ILaunchConfigurationWorkingCopy launchConfigWC = configType.newInstance(null, launchName);

        String projName = aApplication.getModel().getApplicationRoot().getProject().getName();
        String pathToAppServerPy = getPathToAppServerPy();
        String pathToGaeApp = getPathToGaeApplication(aApplication);
        String programArguments = getCommandLineArguments(aApplication);
        String vmArgs = "";

        launchConfigWC.setAttribute(Constants.ATTR_PROJECT, projName);
        launchConfigWC.setAttribute(Constants.ATTR_RESOURCE_TYPE, IResource.FILE);
        launchConfigWC.setAttribute(Constants.ATTR_INTERPRETER, Constants.ATTR_INTERPRETER_DEFAULT);

        launchConfigWC.setAttribute(Constants.ATTR_LOCATION, pathToAppServerPy);
        launchConfigWC.setAttribute(Constants.ATTR_WORKING_DIRECTORY, pathToGaeApp);
        launchConfigWC.setAttribute(Constants.ATTR_PROGRAM_ARGUMENTS, programArguments);
        launchConfigWC.setAttribute(Constants.ATTR_VM_ARGUMENTS, vmArgs);

        launchConfigWC.setAttribute(IDebugUIConstants.ATTR_LAUNCH_IN_BACKGROUND, true);
        launchConfigWC.setAttribute(DebugPlugin.ATTR_CAPTURE_OUTPUT, true);

        if (aSaveConfig)
            config = launchConfigWC.doSave();
        else
            config = launchConfigWC;

        return config;
    }

    /**
     * Gets the command line arguments for the given application.
     *
     * @param aApplication
     */
    protected static String getCommandLineArguments(ZGaePythonApplication aApplication)
    {
        ZGaeApplicationModel model = aApplication.getModel();
        String pathToGaeApp = getPathToGaeApplication(aApplication);

        Map<String, Object> cmdLineParams = new LinkedHashMap();
        if (model.isDevDebug())
            cmdLineParams.put("debug", null);
        cmdLineParams.put("address", model.getDevAddress());
        cmdLineParams.put("port", model.getDevPort());
        // FIXME put in these two paths - based on the application name or some .metadata location
        //cmdLineParams.put("datastore_path", null);
        //cmdLineParams.put("history_path", null);
        if (model.isDevRequireIndexes())
            cmdLineParams.put("require_indexes", null);
        cmdLineParams.put("smtp_host", model.getDevSMTPHost());
        cmdLineParams.put("smtp_port", model.getDevSMTPPort());
        cmdLineParams.put("smtp_user", model.getDevSMTPUser());
        cmdLineParams.put("smtp_password", model.getDevSMTPPassword());
        if (model.isDevEnableSendmail())
            cmdLineParams.put("enable_sendmail", null);
        if (model.isDevShowMailBody())
            cmdLineParams.put("show_mail_body", null);
        cmdLineParams.put("auth_domain", model.getDevAuthDomain());
        if (model.isDevDebugImports())
            cmdLineParams.put("debug_imports", null);
        if (model.isDevDisableStaticCaching())
            cmdLineParams.put("disable_static_caching", null);

        StringBuffer cmdLineBuf = new StringBuffer();
        synchronized (cmdLineBuf)
        {
            for (Map.Entry<String, Object> cmdLineEntry : cmdLineParams.entrySet())
            {
                String cmdLineOption = cmdLineEntry.getKey();
                Object cmdLineValue = cmdLineEntry.getValue();
                if (cmdLineValue == null)
                {
                    cmdLineBuf.append("--");
                    cmdLineBuf.append(cmdLineOption);
                    cmdLineBuf.append(" ");
                }
                else if ("".equals(cmdLineValue))
                {
                    continue;
                }
                else
                {
                    cmdLineBuf.append("--");
                    cmdLineBuf.append(cmdLineOption);
                    cmdLineBuf.append("=");
                    cmdLineBuf.append(cmdLineValue);
                    cmdLineBuf.append(" ");
                }
            }
            cmdLineBuf.append("\"");
            cmdLineBuf.append(pathToGaeApp);
            cmdLineBuf.append("\"");
        }

        return cmdLineBuf.toString();
    }

    /**
     * Returns the path to the Google App Engine application.  This path is a
     * directory, and is found by using the app.yaml file's parent container
     * path.
     */
    private static String getPathToGaeApplication(ZGaePythonApplication aApplication)
    {
        IPath pathToProject = aApplication.getModel().getApplicationRoot().getLocation();
        return pathToProject.toString();
    }

    /**
     * Returns the path to the dev_appserver.py python module.
     */
    private static String getPathToAppServerPy() throws IOException
    {
        File sdkPath = new File(ZGaeSDK.getInstance().getLocation(), IZGaeSDKConstants.SDK_FOLDER_NAME);
        return new File(sdkPath, "dev_appserver.py").getCanonicalPath();
    }

    /**
     * Creates a launch configuration to use for updating the given python
     * application.
     *
     * @param aApplication
     * @throws Exception
     */
    public static ILaunchConfiguration createLaunchConfigurationForAppUpdate(
            ZGaePythonApplication aApplication) throws Exception
    {
        // Make sure that the gaedt python scripts exist in the SDK directory.
        ensureGaedtScriptsExist();

        ILaunchConfigurationType configType = getLaunchConfigType();
        ILaunchConfiguration config = null;

        // Create a new launch configuration working copy for this type
        String launchName = "__gae-update__" + aApplication.getModel().getName();
        ILaunchConfigurationWorkingCopy launchConfigWC = configType.newInstance(null, launchName);

        String projName = aApplication.getModel().getApplicationRoot().getProject().getName();
        String pathToAppCfgPy = getPathToGaedtAppCfgPy();
        String pathToGaeApp = getPathToGaeApplication(aApplication);
        String programArguments = getCommandLineArgumentsForAppUpdate(aApplication);
        String vmArgs = "";
        Map envVariables = getEnvironmentVariablesForAppUpdate();

        launchConfigWC.setAttribute(Constants.ATTR_PROJECT, projName);
        launchConfigWC.setAttribute(Constants.ATTR_RESOURCE_TYPE, IResource.FILE);
        launchConfigWC.setAttribute(Constants.ATTR_INTERPRETER, Constants.ATTR_INTERPRETER_DEFAULT);

        launchConfigWC.setAttribute(Constants.ATTR_LOCATION, pathToAppCfgPy);
        launchConfigWC.setAttribute(Constants.ATTR_WORKING_DIRECTORY, pathToGaeApp);
        launchConfigWC.setAttribute(Constants.ATTR_PROGRAM_ARGUMENTS, programArguments);
        launchConfigWC.setAttribute(Constants.ATTR_VM_ARGUMENTS, vmArgs);

        launchConfigWC.setAttribute(ILaunchManager.ATTR_ENVIRONMENT_VARIABLES, envVariables);

        launchConfigWC.setAttribute(IDebugUIConstants.ATTR_LAUNCH_IN_BACKGROUND, true);
        launchConfigWC.setAttribute(DebugPlugin.ATTR_CAPTURE_OUTPUT, true);

        config = launchConfigWC;
        return config;
    }

    /**
     * Ensures that the _gaedt_*.py scripts exist in the SDK directory.
     */
    private static void ensureGaedtScriptsExist() throws Exception
    {
        ensureGaedtScriptsExist("_gaedt_appcfg.py");
        ensureGaedtScriptsExist("_gaedt_input.py");
    }

    /**
     * Ensures that the named _gaedt_*.py script exists.
     *
     * @param aGaedtScriptName
     * @throws Exception
     */
    private static void ensureGaedtScriptsExist(String aGaedtScriptName) throws Exception
    {
        File sdkPath = new File(ZGaeSDK.getInstance().getLocation(), IZGaeSDKConstants.SDK_FOLDER_NAME);
        File gaedtScriptFile = new File(sdkPath, aGaedtScriptName);

        URL gaedtScriptBundleEntry = ZGaeServicesActivator.getDefault().getBundle().getEntry("support/" + aGaedtScriptName);
        InputStream iStream = null;
        OutputStream oStream = null;

        try
        {
            iStream = gaedtScriptBundleEntry.openStream();
            oStream = new FileOutputStream(gaedtScriptFile);
            ZGaeFileUtil.copy(iStream, oStream);
        }
        finally
        {
            ZGaeCloser.close(iStream);
            ZGaeCloser.close(oStream);
        }
    }

    /**
     * Gets the command line parameters used to update an application.
     *
     * @param aApplication
     */
    private static String getCommandLineArgumentsForAppUpdate(ZGaePythonApplication aApplication)
    {
        String pathToGaeApp = getPathToGaeApplication(aApplication);

        StringBuffer cmdLineBuf = new StringBuffer();
        synchronized (cmdLineBuf)
        {
            cmdLineBuf.append("--verbose update ");
            cmdLineBuf.append("\"");
            cmdLineBuf.append(pathToGaeApp);
            cmdLineBuf.append("\"");
        }

        return cmdLineBuf.toString();
    }

    /**
     * Gets the map of environment variables to use when doing an app update.
     */
    private static Map getEnvironmentVariablesForAppUpdate()
    {
        int servicePort = ZGaeUserInputService.getInstance().getServicePort();
        return Collections.singletonMap("GAEDT_USER_INPUT_SERVICE_PORT", String.valueOf(servicePort));
    }

    /**
     * Returns the path to the _gaedt_appcfg.py file.
     */
    private static String getPathToGaedtAppCfgPy() throws IOException
    {
        File sdkPath = new File(ZGaeSDK.getInstance().getLocation(), IZGaeSDKConstants.SDK_FOLDER_NAME);
        return new File(sdkPath, "_gaedt_appcfg.py").getCanonicalPath();
    }
}
